Skip to content

Mobile terminal: flash fix, key bar, scroll momentum - #1

Merged
karngyan merged 7 commits into
mainfrom
worktree-mobile-flash-keybar
Aug 8, 2026
Merged

Mobile terminal: flash fix, key bar, scroll momentum#1
karngyan merged 7 commits into
mainfrom
worktree-mobile-flash-keybar

Conversation

@karngyan

@karngyan karngyan commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Three mobile-terminal improvements, following up the visual-viewport / pty-sizing branch.

Keyboard-open flash

relayout scaled the whole surface by a uniform factor whenever the pty stopped fitting in either axis, so a keyboard taking half the pane's height pinched the width too for the ~150ms settle window. The surface now renders one-to-one whenever every column fits — rows-only overflow clips briefly behind the keyboard instead, and the width never moves. Scaling remains for column overflow, where clipping would amputate lines.

On-screen key bar (touch devices only)

ctrl esc tab ← ↓ ↑ → chips floated over the terminal's bottom edge, in the chip style of the existing control strip.

  • Arrows follow DECCKM through a new Emulator.applicationCursorKeys() seam method: CSI at a shell, SS3 once vim or another full-screen program has asked — same as a hardware keyboard through xterm.
  • Ctrl latches: one press arms it for the next key, typed or bar, spent whether or not it folds. ctrl + c sends 0x03.
  • Presses ride pointerdown with the default prevented, so xterm keeps focus and the soft keyboard stays open; assistive tech activates through a detail === 0 click guard.
  • Bar keys share the typed input's ref/mute gate, and the inset reserves the bar's room so it covers no rows.

Scroll momentum

Flicks glide: release velocity is read from the last 100ms of drag samples (so touch-pause-flick reads the flick, not the pause) and decays at UIKit's 0.998-per-millisecond rate, emitting whole lines with the fraction carried. A finger back on the glass, a pinch, touchcancel, or unmount kills it.

Review trail

Per-task reviews plus a whole-branch pass; three fix rounds landed AT activation, the Ctrl-spend test (mutation-proven), and the flick-window trim. Suite: 599 web tests + real-build Tailwind-scanner guard + tsc clean.

Before release: real-device pass — clip direction on keyboard open, bar fit on a 320px phone, VoiceOver/TalkBack double-tap, glide feel.

🤖 Generated with Claude Code

karngyan and others added 7 commits August 8, 2026 16:48
Rows-only overflow now renders one-to-one with the bottom rows briefly
clipped instead of scaling the whole surface down: the uniform factor
pinched both axes for the settle window, a visible lurch on every
keyboard open. Scaling remains for column overflow, where clipping
would amputate lines.
Arrows follow DECCKM (CSI for a shell, SS3 for vim and friends),
Ctrl-arrows are the modified CSI form either way, and a sticky Ctrl
folds the next typed byte onto its control code. Pure functions ahead
of the bar that will press them.
Esc, tab, a sticky ctrl and the four arrows, floated over the
terminal's bottom edge on coarse-pointer devices only. Arrows follow
DECCKM through a new emulator seam method, ctrl folds the next
keystroke on the input path, and presses ride pointerdown with the
default prevented so the soft keyboard stays open. The inset reserves
the bar's room, so it covers no rows.
VoiceOver's and TalkBack's double-tap synthesise a click and dispatch
no pointer event at all, and so does Enter on a hybrid device's
keyboard — so a bar wired to pointerdown alone offered a named,
aria-pressed control that answered nothing, on exactly the devices it
exists for. Each chip gains an onClick guarded on detail === 0, which
is true only of a synthesised click, so a finger's own follow-up click
still cannot send the key twice. Pointerdown and its prevented default
are untouched: the soft keyboard stays open as before.

Also pins the latched Ctrl's other half — the arming is spent even on
a keystroke that cannot fold. Nothing distinguished that from moving
the spend inside the fold check, which would latch Ctrl for good after
a digit.
Drags scrolled line-for-line and stopped dead at the lift, which reads
as rigid to a thumb calibrated by every other scrolling surface on a
phone. A release velocity now decays at UIKit's 0.998-per-millisecond
rate, emitting whole lines with the fraction carried, and dies at the
next touch, a pinch, or unmount.
The release velocity was measured from touchdown, because the anchor
sample stayed in the window for the whole of any flick shorter than six
moves. Touching the glass to stop a glide, hesitating, then flicking is
the commonest gesture there is, and the hesitation was being averaged
into the answer: a 125 lines/s flick after a 200ms pause read as 17 and
glided an eighth as far. Samples older than 100ms are now dropped, two
always kept so a short flick still reads.
touchcancel shared the lift handler, and a drag the system takes away —
the notification shade, an alert, a call — leaves samples that read as a
flick: fast, recent, promptly followed by an event. The scrollback would
coast on with nothing on the glass. It gets its own handler now, one that
clears the gesture and reaches startGlide by no path.

The latched Ctrl was spent through state alone, so the ref the input path
reads stayed armed until React flushed. Two onData deliveries in one task
both folded, and one press of ctrl produced two control codes. The ref is
now cleared by hand beside both setCtrlArmed(false) sites.

applicationCursorKeys reached into a possibly-disposed terminal without
the guard its three neighbours carry — harmless in the xterm shipped
today, which is a fact about xterm's internals rather than about this
seam. Two comments that named "the three below" and "the last three" had
outlived the member sets they counted, and now say what is there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@karngyan
karngyan merged commit 77bc891 into main Aug 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant